﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ABB.Common.DataModel;
using System.Data;
using ABB.Common.BusinessLogic.Helper;
using ABB.Common.BusinessLogic.Contracts;

namespace ABB.Common.BusinessLogic.Reporting
{
    /// <summary>
    /// Exception, wenn eine zu füllende <see cref="System.Data.DataTable"/> ein nicht
    /// übereinstimmendes Schema mit der übergebenen <see cref="ReportColumnMapping"/>
    /// Auflistung besitzt.
    /// </summary>
    internal class InvalidTableSchemaOrMappingException : Exception
    {
        public InvalidTableSchemaOrMappingException() { }
        public InvalidTableSchemaOrMappingException(string message) : base(message) { }
        public InvalidTableSchemaOrMappingException(string message, Exception inner) : base(message, inner) { }
        protected InvalidTableSchemaOrMappingException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    /// <summary>
    /// Füllt <see cref="System.Data.DataTable"/> gemäss dem übergebenen Mapping mit den
    /// übergebenen Daten.
    /// </summary>
    internal class ReportDataTableFiller
    {
        private readonly ProjectPlanningDataSet.ReportRow m_Report;
        private readonly ProjectPlanningDataSet.UserRow m_CurrentUser;

        private readonly ReportColumnMapping m_ReportMapping;
        private readonly IReportDataLoader m_DataLoader;
        private readonly ReportFilterEvaluator m_FilterEvaluator;
        private readonly ReportSortEvaluator m_SortEvaluator;

        #region C'tors

        /// <summary>
        /// Öffentlicher Konstruktor
        /// </summary>
        /// <param name="report"></param>
        /// <param name="mapping"></param>
        /// <param name="user"></param>
        public ReportDataTableFiller(ProjectPlanningDataSet.ReportRow report,
                                     ReportColumnMapping mapping,
                                     IReportDataLoader dataLoader,
                                     ProjectPlanningDataSet.UserRow user)
        {
            if (report == null) throw new ArgumentNullException("report");
            if (mapping == null) throw new ArgumentNullException("mapping");
            if (dataLoader == null) throw new ArgumentNullException("dataLoader");
            if (user == null) throw new ArgumentNullException("user");

            this.m_Report = report;
            this.m_ReportMapping = mapping;
            this.m_DataLoader = dataLoader;
            this.m_CurrentUser = user;

            this.m_FilterEvaluator = new ReportFilterEvaluator(this.m_Report,
                                            this.m_CurrentUser, this.m_DataLoader);
            this.m_SortEvaluator = new ReportSortEvaluator(this.m_DataLoader, this.m_Report, this.m_CurrentUser);
        }

        #endregion

        /// <summary>
        /// Füllt die übergebenen Tabelle mit dem übergebenen Schema.
        /// </summary>
        /// <param name="table">Die zu füllende <see cref="DataTable"/>.</param>
        /// <exception cref="InvalidTableSchemaOrMappingException">Wird geworfen,
        /// wenn das Schema der Tabelle nicht mit dem <see cref="ReportColumnMapping"/>
        /// übereinstimmt.</exception>
        public void FillTable(DataTable table)
        {
            if (table == null) throw new ArgumentNullException();

            // Die Anzahl Spalten in der Tabelle müsste der Anzahl Spalten
            // im Mapping + 1 entsprechen (die erste Spalte ist nicht im Mapping
            // - der 'Sales order name').
            if (table.Columns.Count != this.m_ReportMapping.Count)
                throw new InvalidTableSchemaOrMappingException();

            var filteredLots = this.m_FilterEvaluator.GetFilteredLots();
            var sortedLots = this.m_SortEvaluator.GetSortedList(filteredLots);

            // Fill the table
            foreach (var lot in sortedLots)
            {
                DataRow newRow = table.NewRow();

                foreach (DataColumn column in table.Columns)
                {
                    bool hasChanged = false;
                    newRow[column] = this.m_DataLoader.GetValueForLotByReportColumn(
                                                        lot, this.m_ReportMapping[column],
                                                        this.m_CurrentUser, out hasChanged);
                    if (hasChanged)
                        newRow.SetColumnError(column, "Changed");

                }

                table.Rows.Add(newRow);
            }
        }

    }
}
